#include "AlgorithmProcessor.h"
#include "AnalyticalProcessor.h"
#include "RayTracingProcessor.h"
#include <algorithm/analytical_model/algorithm/pipeline/AnalyticalPipelineParam.h>
#include <algorithm/analytical_model/io/json/converter/AllConverters.h>
#include <algorithm/analytical_model/io/json/transform/TransformJSON.h>
#include <algorithm/rayTracing/dataLoader/SceneConfiguration.h>
#include <algorithm/rayTracing/dataLoader/SceneField.h>
#include <algorithm/rayTracing/dataLoader/SceneLoader.h>
#include <algorithm/rayTracing/rayTracingPipeline/RayTracingPipeline.h>
#include <algorithm/shared/tool/checkFilePath/CheckFilePath.h>
#include <ui/scene/Scene.h>
#include <ui/window/dataVisualizationWindow/ColorMap.h>
#include <ui/window/dataVisualizationWindow/FluxMapParameter.h>
#include <ui/window/dataVisualizationWindow/SceneRendererAdapter.h>
#include <ui/window/simulationWindow/RayTracingParameter.h>

#include <boost/gil.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <boost/gil/image.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/io/io.hpp>
#include <boost/gil/typedefs.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/iostreams/stream.hpp>

#include <QColor>
#include <QImage>
#include <QLinearGradient>
#include <QPainter>
#include <QQmlApplicationEngine>

#include <bit>
#include <utility>

using namespace solar;
using namespace boost::gil;

AlgorithmProcessor::AlgorithmProcessor(std::shared_ptr<Scene> scene,
                                       std::shared_ptr<RayTracingParameter> simulation_paras,
                                       std::shared_ptr<FluxMapParameter> flux_map_paras,
                                       QQmlApplicationEngine* qml_engine)
    : scene_(std::move(scene)), simulation_paras_(std::move(simulation_paras)),
      flux_map_paras_(std::move(flux_map_paras)), qml_engine_(qml_engine)

{
    raytracing_pipeline_ = std::make_shared<RayTracingPipeline>();
    ray_tracing_processor_ = std::make_shared<RayTracingProcessor>();
    analytical_pipeline_param_ = std::make_shared<AnalyticalPipelineParam>();
    ray_tracing_processor_->setRayTracingPipeline(raytracing_pipeline_);
}

// QString AlgorithmProcessor::getInputFilePath()
// {
//     return input_file_path_;
// }

// void AlgorithmProcessor::setInputFilePath(QString input_file_path)
// {
//     /**
//      * 从ui传过来的input_file_path是url格式的，路径会有前缀“file://”，需要把前缀删去
//     */
//     std::string std_input_file_path = input_file_path.toStdString();
//     CheckFilePath::invertUrlToFilePath(std_input_file_path);

//     if(CheckFilePath::isTheFileValid(std_input_file_path))
//     {
//         input_file_path = QString::fromStdString(std_input_file_path);
//     }
//     else{
//         std::cout <<"ERROR::AlgorithmProcessor::setInputFilePath::file psth is not valid:"
//                     << std_input_file_path << std::endl;
//         return;
//     }

//     if(input_file_path_ != input_file_path)
//     {
//         input_file_path_ = input_file_path;
//         emit inputFilePathChanged();
//         qDebug() << "********** changed input_file_path to: " << input_file_path_ ;
//     }
// }

auto AlgorithmProcessor::getCudaDataImage() -> float*
{
    return RayTracingPipeline::result_data_->device_data_after_smooth;
}
auto AlgorithmProcessor::getFluxMapData() -> cuda::inter_op::Uchar4*
{
    return std::bit_cast<cuda::inter_op::Uchar4*>(RayTracingPipeline::result_data_->device_fluxmap);
}

auto AlgorithmProcessor::runRayTracing() -> bool
{
    // std::cout <<"input_file_path="<< input_file_path_.toStdString() << std::endl;

    // bool file_path_valid = CheckFilePath::isTheFileValid(input_file_path_.toStdString());
    // if( file_path_valid )   // 若文件路径合法，则从文件中读取场景数据，否则从scene等读取场景数据
    // {
    //     std::cout <<"------------use json file\n";
    //     ray_tracing_processor_->updateRayTracingPipelineData(input_file_path_.toStdString(),
    //     simulation_paras_);
    // }
    // else{       //
    //     ray_tracing_processor_->updateRayTracingPipelineData(scene_, simulation_paras_);
    // }
    // std::shared_ptr<RayTracingProcessor> raytracing_processor =
    // std::make_shared<RayTracingProcessor>();
    // raytracing_processor->updateRayTracingPipelineData(scene_,simulation_paras_,
    // raytracing_pipeline_);
    ray_tracing_processor_->updateRayTracingPipelineData(scene_, simulation_paras_);

    // ray_tracing_processor_->updateRayTracingPipelineData(scene_, simulation_paras_);

    auto start_time = std::chrono::high_resolution_clock::now();
    auto end_time = start_time;
    long long elapsed = 0;
    qDebug() << "111";
    raytracing_pipeline_->rayTracing();

    end_time = std::chrono::high_resolution_clock::now();
    elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
    std::cout << " whole raytracing process took " << elapsed << " microseconds (UI)." << std::endl;

    updateFluxMapPara();

    algorithm_type_ = algorithm_raytracing_;

    return true;
}

auto AlgorithmProcessor::runAnalyticalFunction() -> bool
{
    // bool use_file_path = CheckFilePath::isTheFileValid(input_file_path_.toStdString());
    // if(use_file_path)   // 若文件路径合法，则从文件中读取场景数据，否则从scene等读取场景数据
    // {

    // }
    // else{       //

    // }

    algorithm_type_ = algorithm_analytical_;

    return true;
}

auto AlgorithmProcessor::runFile() -> bool
{
    std::cout << "AlgorithmProcessor::runfile\n";

    auto start_time = std::chrono::high_resolution_clock::now();
    auto end_time = start_time;
    long long elapsed = 0;

    const auto* value_ptr = scene_json_value_.if_object();
    if (value_ptr == nullptr)
    {
        return false;
    }

    if (const auto* algorithm_value = value_ptr->if_contains("algorithm");
        algorithm_value != nullptr)
    {
        auto algorithm_type_str = algorithm_value->get_string();
        if (algorithm_raytracing_str_ == algorithm_type_str)
        {
            raytracing_pipeline_->rayTracing(true, json_file_path_);

            // ray_tracing_processor_->updateRayTracingPipelineData(std_file_path,
            // simulation_paras_);
            // raytracing_pipeline_->setSceneField(ray_tracing_processor_->getField());
            // raytracing_pipeline_->setConfiguration(ray_tracing_processor_->getConfig());
            // raytracing_pipeline_->rayTracing();

            // std::cout <<"================scene loader====================\n";
            // raytracing_pipeline_->loadDataFromJSON(std_file_path);
            // auto field_sceneloader = raytracing_pipeline_->getSceneField();
            // auto config_sceneloader = raytracing_pipeline_->getConfiguration();

            // std::cout <<"================processor====================\n";
            // ray_tracing_processor_->updateRayTracingPipelineData(std_file_path,
            // simulation_paras_); auto field_processor = ray_tracing_processor_->getField(); auto
            // config_processor = ray_tracing_processor_->getConfig();

            // std::cout <<"===================compare config begin=============================\n";
            // std::cout <<"-------config_sceneloader----\n";
            // config_sceneloader->test();
            // std::cout << "------config_processor------\n";
            // config_processor->test();
            // std::cout <<"===================compare config end  =============================\n";

            // std::cout <<"===================compare field begin=============================\n";
            // std::cout <<"-------field_sceneloader----\n";
            // field_sceneloader->test();
            // std::cout << "------field_processor------\n";
            // field_processor->test();
            // std::cout <<"===================compare field end  =============================\n";

            // raytracing_pipeline_->rayTracing();

            end_time = std::chrono::high_resolution_clock::now();
            elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time)
                          .count();
            std::cout << " whole raytracing process took " << elapsed << " microseconds (File)."
                      << std::endl;
        }
        else if (algorithm_analytical_str_ == algorithm_type_str)
        {
        }
        else
        {
            return false;
        }
    }

    updateFluxMapPara();

    return true;
}

auto AlgorithmProcessor::plotFluxMap() -> bool
{
    auto start_time = std::chrono::high_resolution_clock::now();
    auto end_time = start_time;
    long long elapsed = 0;
    if (algorithm_type_ == 0)
    {
        std::cout << "======== AlgorithmProcessor::plotFluxMap ==============\n";

        // ray_tracing_processor_->plotFluxMap();
        /**
         * cudatexture在算法计算结束后即可开始进行，这里的plotfluxmap，应该是把纹理保存成图像
         */
        raytracing_pipeline_->plotFluxMap();
    }
    end_time = std::chrono::high_resolution_clock::now();
    elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time).count();
    std::cout << " plot flux map took " << elapsed << " microseconds to ray tracing." << std::endl;

    return true;
}

void AlgorithmProcessor::updateFluxMapPara()
{
    auto result_data = RayTracingPipeline::result_data_;

    flux_map_paras_->cppSetPixelNumX(result_data->resolution.x);
    flux_map_paras_->cppSetPixelNumY(result_data->resolution.y);
    flux_map_paras_->cppSetTotalPower(result_data->total_power);
    flux_map_paras_->cppSetMaximumFlux(result_data->maximum_flux);
    flux_map_paras_->cppSetMaximumFluxLocationX(result_data->maximum_location.x);
    flux_map_paras_->cppSetMaximumFluxLocationY(result_data->maximum_location.y);
}

void AlgorithmProcessor::setJsonFilePath(const QString& path)
{
    auto std_file_path = path.toStdString();
    CheckFilePath::invertUrlToFilePath(std_file_path);
    this->json_file_path_ = std_file_path;
    updateAnalyticalPipelineParam(io::JSONType{});
    updateScene();
}

void AlgorithmProcessor::updateAnalyticalPipelineParam(const io::JSONType&)
{
    boost::iostreams::mapped_file_source input_buffer(json_file_path_);
    input_buffer.close();
    input_buffer.open(json_file_path_);
    boost::json::stream_parser parser;
    parser.reset();
    parser.write(input_buffer.data(), input_buffer.size());
    parser.finish();
    auto json_value = parser.release();
    const auto* value_ptr = json_value.if_object();
    if (value_ptr == nullptr)
    {
        return;
    }
    scene_json_value_ = json_value;

    // TODO: Check JSON input to avoid crashing

    this->analytical_pipeline_param_ = std::make_shared<AnalyticalPipelineParam>(
        loadAnalyticalPipelineParam(io::JSONType{}, json_value));
}

void AlgorithmProcessor::updateScene()
{
    auto* scene_renderer_adapter = dynamic_cast<SceneRendererAdapter*>(
        qml_engine_->rootObjects().first()->findChild<QObject*>("scene_renderer_adapter"));
    scene_renderer_adapter->updateScene(analytical_pipeline_param_.get());
}
